﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ExpressionToWhere
{
    /// <summary>
    /// 处理worker.Name.StartsWith("%xxx")整个表达式
    /// </summary>
    internal class MethodCallExpressionVisitor : BaseExpressionVisitor
    {
        public MethodCallExpressionVisitor(bool searchNull, string dbType = "SqlServer") : base(searchNull, dbType)
        {

        }

        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            string symbol = string.Empty;
            string methodName = node.Method.Name;
            switch (methodName)
            {
                case "Equals":
                    symbol = "= {0}";
                    break;
                case "StartsWith":
                    symbol = "like {0}";
                    break;
                case "EndsWith":
                    symbol = "like {0}";
                    break;
                case "Contains":
                    symbol = "like {0}";
                    break;
                case "In":
                    symbol = "in {0}";
                    break;
                case "NotIn":
                    symbol = "not in {0}";
                    break;
                default:
                    throw new NotSupportedException($"Not support method:{node.Method.Name}");
            }

            if (methodName == "Equals")
            {
                HandleEqual(node, symbol);
            }
            else if (methodName == "StartsWith" || methodName == "EndsWith" || methodName == "Contains")
            {
                HandleLike(node, symbol, methodName);
            }
            else if (methodName == "In" || methodName == "NotIn")
            {
                HandleIn(node, symbol);
            }

            return node;
        }

        private void HandleEqual(MethodCallExpression node, string symbol)
        {
            if (node.Object is MemberExpression)
            {
                MemberExpressionVisitor visitor = new MemberExpressionVisitor();
                visitor.Visit(node.Object);
                string fieldName = visitor.GetResult().ToString();
                string parameterName = ExpressionProcesser.GetPatameterName(visitor.MemberInfo);
                object parameterValue = ExpressionProcesser.GetConstant(node.Arguments[0]);
                sb.Append(string.Format($"{fieldName} {symbol}", $"@{parameterName}"));
                ExpressionProcesser.Parameters.Add($"@{parameterName}", parameterValue);
            }
            else
            {
                throw new NotSupportedException($"Not Support Expression:{node.Object}");
            }
        }

        private void HandleLike(MethodCallExpression node, string symbol, string methodName)
        {
            if (node.Object is MemberExpression)
            {
                MemberExpressionVisitor visitor = new MemberExpressionVisitor();
                visitor.Visit(node.Object);
                string fieldName = visitor.GetResult().ToString();
                string parameterName = ExpressionProcesser.GetPatameterName(visitor.MemberInfo);
                object parameterValue = ExpressionProcesser.GetConstant(node.Arguments[0]);
                if (parameterValue == null) return;
                if (parameterValue != null)
                {
                    if (string.Equals("SqlServer", dbType, StringComparison.OrdinalIgnoreCase))
                    {
                        //[SqlServer]parameterValue=%xxx时，将worker.Name like '%xxx%'转换成worker.Name like '[%]xxx%'
                        parameterValue = parameterValue.ToString().Replace("%", "[%]");
                        //[SqlServer]parameterValue=_xxx时，将worker.Name like '_xxx%'转换成worker.Name like '[_]xxx%'
                        parameterValue = parameterValue.ToString().Replace("_", "[_]");
                    }
                    else if(string.Equals("MySql", dbType, StringComparison.OrdinalIgnoreCase))
                    {
                        //[MySql]parameterValue=%xxx时，将worker.Name like '%xxx%'转换成worker.Name like '\\%xxx%'
                        parameterValue = parameterValue.ToString().Replace("%", @"\\%");
                        //[MySql]parameterValue=_xxx时，将worker.Name like '_xxx%'转换成worker.Name like '\\_xxx%'
                        parameterValue = parameterValue.ToString().Replace("_", @"\\_");
                    }
                }
                if (methodName == "StartsWith")
                {
                    parameterValue = parameterValue + "%";
                }
                else if (methodName == "EndsWith")
                {
                    parameterValue = "%" + parameterValue;
                }
                else if (methodName == "Contains")
                {
                    parameterValue = "%" + parameterValue + "%";
                }
                sb.Append(string.Format($"{fieldName} {symbol}", $"@{parameterName}"));
                ExpressionProcesser.Parameters.Add($"@{parameterName}", parameterValue);
            }
            else
            {
                throw new NotSupportedException($"Not Support Expression:{node.Object}");
            }
        }

        private void HandleIn(MethodCallExpression node, string symbol)
        {
            Expression argu1 = node.Arguments[0];
            Expression argu2 = node.Arguments[1];
            if (argu1 is MemberExpression)
            {
                MemberExpressionVisitor visitor = new MemberExpressionVisitor();
                visitor.Visit(argu1);
                string fieldName = visitor.GetResult().ToString();
                string parameterName = ExpressionProcesser.GetPatameterName(visitor.MemberInfo, Config.InConditionPrefix);
                object parameterValue = ExpressionProcesser.GetConstant(argu2);
                if (parameterValue == null || (parameterValue is List<string> list && list.Count < 1)) return;
                sb.Append(string.Format($"{fieldName} {symbol}", $"@{parameterName}"));
                ExpressionProcesser.Parameters.Add($"@{parameterName}", parameterValue);
            }
            else
            {
                throw new NotSupportedException($"Not Support MethodCallExpression:{node}");
            }
        }


    }
}
